#!/usr/bin/env python 
# -*- coding: utf-8 -*- 
# @Time : 2020/3/31 6:08 下午 
# @Author : Aries 
# @Site :  
# @File : bilibili.py 
# @Software: PyCharm
import getopt
import re
import sys

import requests
from fake_useragent import UserAgent
from pymongo import MongoClient

HEADERS = {
    'user-agent': UserAgent().random
}

# 条件查询结果列表
result_list = []
connect = MongoClient(host='localhost', port=27017)
connect_db = connect['bilibili']


# 访问指定的URL地址，采用随机header
def get_response(url, headers=None):
    if headers is None:
        headers = HEADERS
    try:
        response = requests.get(url, headers=headers)
        if response.status_code == 200:
            return response
        return None
    except Exception as e:
        return None


# 获取查询结果列表
def get_search_list(keyword=None, page=1, order='totalrank', duration=0, tids=0):
    """
    获取条件查询结果列表
    keyword: 关键字
    page: 查询第几页
    order:排序
        综合排序：totalrank
        最新发布：pubdate
        最多点击：click
        最多弹幕：dm
        最多收藏：stow
    duration:时长
        全部时长：0
        10分钟以下：1
        10~30分钟：2
        30~60分钟：3
        60分钟以上：4
    tids: 分区类型ID
    """
    url_format = r'https://api.bilibili.com/x/web-interface/search/type' \
                 r'?search_type=video&jsonp=jsonp&keyword={}&order={}&duration={}&tids={}&page={}'
    url = url_format.format(keyword, order, duration, tids, page)

    response = get_response(url)
    json = response.json()
    if json['code'] != 0:
        return

    data = json['data']

    # 当前页数
    page = data['page']
    # 总页数
    num_pages = data['numPages']

    for result in data['result']:
        """
        {
            "type": "video",
            "id": 14184325,
            "author": "黑马程序员-",
            "mid": 37974444,
            "typeid": "39",
            "typename": "演讲•公开课",
            "arcurl": "http://www.bilibili.com/video/av14184325",
            "aid": 14184325,
            "bvid": "BV1ex411x7Em",
            "title": "<em class=\"keyword\">Python</em>教程_600集<em class=\"keyword\">Python</em>从入门到精通教程（懂中文就能学会）",
            "description": "分享java教程，java学习视频，Python教程，Python学习教程，PHP教程，PHP学习教程。本套教程为传智播客旗下高端教育品牌黑马程序员出品。\n目录大纲：\n本套教程15天\n1-3   天内容为Linux基础命令\n4-13  天内容为Python基础教程\n14-15 天内容为 飞机大战项目演练\n视频教程出自http://yun.itheima.com/course/273.html?bili",
            "arcrank": "0",
            "pic": "//i0.hdslb.com/bfs/archive/3992190257601fceb4725f3df91cc94b8a8e37cd.jpg",
            "play": 4990686,
            "video_review": 108581,
            "favorites": 326744,
            "tag": "黑马,Python入门教程,入门,Python爬虫,小白,编程,教育,语言,Python,Python教程",
            "review": 3540,
            "pubdate": 1504581916,
            "senddate": 1565317791,
            "duration": "3289:48",
            "badgepay": false,
            "hit_columns": [
                "title",
                "description",
                "tag"
            ],
            "view_type": "",
            "is_pay": 0,
            "is_union_video": 0,
            "rec_tags": null,
            "new_rec_tags": [],
            "rank_score": 114237379
        }
        """

        info = {
            'id': result['id'],
            'author': result['author'],  # up主
            'type_id': result['typeid'],  # 视频类型ID（参数列表中的tids）
            'type_name': result['typename'],  # 视频类型名称
            'arcurl': result['arcurl'],  # 视频组首页视频地址
            'aid': result['aid'],  # 视频AV号
            'bvid': result['bvid'],  # 视频组的ID
            'title': result['title'].replace('<em class="keyword">', '').replace('</em>', ''),  # 视频标题
            'description': result['description'].replace('<em class="keyword">', '').replace('</em>', ''),  # 视频介绍
            'pic': r'http:{}'.format(result['pic']),  # 首页图片地址
            'play': result['play'],  # 播放次数
            'tag': result['tag'],  # 视频标签
            'duration': result['duration'],  # 视频时长=时*60+分:秒
            'pubdate': result['pubdate']  # 视频上传时间（秒）
        }

        videos = get_player_page_list(result['bvid'])
        info['videos'] = videos
        result_list.append(info)

        my_col = connect_db['python']

        if my_col.count_documents({'bvid': info['bvid']}) == 0:
            my_col.insert_one(info)
        print(result_list.__len__(), info['title'])
    if page == num_pages:
        return
    next_page = page + 1
    get_search_list(keyword, page=next_page, order=order, duration=duration, tids=tids)


def get_player_page_list(bvid):
    """
    方法用于获取视频的列表地址
    bvid: 视频组的ID
    """
    url = r'https://api.bilibili.com/x/player/pagelist?bvid={}&jsonp=jsonp'.format(bvid)
    response = get_response(url).json()
    if response['code'] != 0:
        return None
    videos = []
    for result in response['data']:
        videos.append({
            'cid': result['cid'],
            'page': result['page'],
            'part': result['part'],
            'duration': result['duration'],
            'web_link': r'https://www.bilibili.com/video/{}?p={}'.format(bvid, result['page'])
        })
    return videos


# 下载B站视频
def download_video(bvid, p, title):
    """
    下载B站视频
    bvid: 视频组的ID
    p: 视频索引，从 1 开始
    title: 标题
    """

    url = r'https://www.bilibili.com/' + bvid + '?p=' + p
    headers = {
        'Host': 'www.bilibili.com',
        'User-Agent': UserAgent().random,
        'Connection': 'keep-alive',
    }

    res = get_response(url, headers).text

    vid = re.compile(r'"url":"http(.+?)","backup_url":.+?')
    vid_url = 'https' + re.findall(vid, res)[0]

    vid_headers = {
        'Origin': 'https://www.bilibili.com',
        'Referer': url,
        'User-Agent': UserAgent().random,
    }

    video = get_response(vid_url, vid_headers).content
    with open(title + '.mp4', 'wb+') as f:
        f.write(video)
    print('视频：{} 下载完成'.format(title))


def usage():
    """
    @summary: the usage
    @param : None
    @return: None
    """
    print(
        """ The usage of the commands:
           -k        检索条件           必传              e.g. "python"
           -p        检索结果开始页     可选              e.g. 1
           -o        排序方式           可选              e.g. "totalrank"
           -d        时长选项           可选              e.g. 0
           -t        类型选项           可选              e.g. 0

           e.g.
           python bilibili.py -k "python" -p 1 -o "totalrank" -d 0 -t 0
           """
    )


def print_help_exit():
    """
    @summary: print the help
    @param : None
    @return: None
    """
    usage()
    sys.exit(-1)


def parse_options():
    """
    @summary: get the external afferent parameters
    @param : None
    @return: None
    """
    rs = {}
    try:
        opts, args = getopt.getopt(sys.argv[1:], 'k:p:o:d:t')

        if 5 >= len(opts) > 0:
            for name, value in opts:
                if name in "-h":
                    print_help_exit()
                elif name in "-k":
                    if value is not None:
                        rs['keyword'] = value
                    else:
                        print("You did not give mysql_name")
                elif name in "-p":
                    if value is not None:
                        rs['page'] = value
                    else:
                        rs['page'] = 1
                elif name in "-o":
                    if value is not None:
                        rs['order'] = value
                    else:
                        rs['order'] = "totalrank"
                elif name in "-d":
                    if value is not None:
                        rs['duration'] = value
                    else:
                        rs['duration'] = 0
                elif name in "-t":
                    if value is not None:
                        rs['tids'] = value
                    else:
                        rs['tids'] = 0
                else:
                    print_help_exit()
        else:
            print_help_exit()
    except getopt.GetoptError:
        print_help_exit()
    return rs


if __name__ == '__main__':
    options = parse_options()
    keys = options.keys()
    if 'page' not in keys:
        options['page'] = 1
    if 'order' not in keys:
        options['order'] = 'totalrank'
    if 'duration' not in keys:
        options['duration'] = 0
    if 'tids' not in keys:
        options['tids'] = 0

    get_search_list(**options)
    # aid = input('请输入av号：')
    # download_video('BV1ex411x7Em')
